/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;

/**
 * The OPMessagesDispatcher class is a javascript object in charge of dispatching messages between a container slot and its embedded view. It contains necessary
 * callbacks to the view proxy and to the slot proxy, that will relay back messages respectively to the embedded view and the container slot.
 * <p>
 * Message dispatching is done through call to methods {@link OPViewProxy#dispatchMessageToEmbeddedView(String, OPParams, OPCommandCallback)} and
 * {@link OPSlotProxy#dispatchMessageToContainerSlot(String, OPParams, OPCommandCallback)}.
 * <p>
 * The dispatcher is identified by an id, which is the id of the HTML element to which the dispatcher is attached. This generally is the slotId of the slot.
 * <p>
 * OPMessagesDispatcher is a JavaScriptObject, that is a real javascript object mapped to Java thanks to GWT overlay absolutely fantastic technique.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 14 July 2010
 */
public final class OPMessagesDispatcher extends JavaScriptObject {

    /**
     * Create and return a new OPMessagesDispatcher object.
     * 
     * @param id
     *            the id of the element to which this dispatcher is linked. Shouldn't be null, shouldn't be empty.
     * @return the corresponding OPMessagesDispatcher. Cannot be null.
     */
    public final static OPMessagesDispatcher dispatcherFactory(final String id) {
        assert id != null : "parameter id shouldn't be null";
        assert !id.isEmpty() : "parameter id shouldn't be empty";

        return dispatcherFactoryImpl(id);
    }

    /**
     * Real implementation of creating and returning a new OPMessagesDispatcher object.
     * 
     * @param id
     *            the id of the element to which this dispatcher is linked
     * @return the corresponding OPMessagesDispatcher
     */
    private final static native OPMessagesDispatcher dispatcherFactoryImpl(final String id) /*-{
        var dispatcher = {};
        dispatcher.id = id;
        dispatcher.messageFromEmbeddedViewHandlerCallback = null;
        dispatcher.messageFromContainerSlotHandlerCallback = null;
        dispatcher.onEmbeddedViewAttachCallback = null;
        return dispatcher;
    }-*/;

    /**
     * Protected constructor, necessary because it's a JavaScriptObject.
     */
    protected OPMessagesDispatcher() {}

    /**
     * Return the id of this dispatcher. This is the id of the HTML element to which the dispatcher is attached
     * 
     * @return the id of this dispatcher. Cannot be null.
     */
    public final native String getId() /*-{
        return this.id;
    }-*/;

    /**
     * Check if there is an callback registered for message from embedded view handler.
     * 
     * @return true if there is an callback registered for message from embedded view handler, false otherwise.
     */
    public final native boolean isMessageFromEmbeddedViewHandlerCallbackRegistered() /*-{
        return (this.messageFromEmbeddedViewHandlerCallback != null);
    }-*/;

    /**
     * Check if there is an callback registered for message from container slot handler.
     * 
     * @return true if there is an callback registered for message from container slot handler, false otherwise.
     */
    public final native boolean isMessageFromContainerSlotHandlerCallbackRegistered() /*-{
        return (this.messageFromContainerSlotHandlerCallback != null);
    }-*/;

    /**
     * Dispatch a message and its parameters from a container slot to its embedded view. Don't do anything if there is no message handler callback registered
     * for this view.
     * 
     * @param message
     *            the message to dispatch. Shouldn't be null.
     * @param params
     *            the params to pass to the view. Can be empty, can be null.
     */
    public final void dispatchMessageToEmbeddedView(final String message, final OPParamsJS params) {
        assert message != null : "parameter message shouldn't be null";

        dispatchMessageToEmbeddedViewImpl(message, params);
    }

    /**
     * Real implementation of calling the message handler callback. Don't do anything if there is no message handler callback registered for this view.
     * 
     * @param message
     *            the message to dispatch.
     * @param params
     *            the params to pass to the view.
     */
    private final native void dispatchMessageToEmbeddedViewImpl(final String message, final OPParamsJS params) /*-{
        if (this.messageFromContainerSlotHandlerCallback != null) {
            this.messageFromContainerSlotHandlerCallback(message, params);
        }
    }-*/;

    /**
     * Dispatch a message and its parameters from an embedded view to its container slot. Don't do anything if there is no message handler callback registered
     * for this slot.
     * 
     * @param message
     *            the message to dispatch. Shouldn't be null.
     * @param params
     *            the params to pass to the slot. Can be empty, can be null.
     */
    public final void dispatchMessageToContainerSlot(final String message, final OPParamsJS params) {
        assert message != null : "parameter message shouldn't be null";

        dispatchMessageToContainerSlotImpl(message, params);
    }

    /**
     * Real implementation of calling the message handler callback. Don't do anything if there is no message handler callback registered for this slot.
     * 
     * @param message
     *            the message to dispatch.
     * @param params
     *            the params to pass to the slot.
     */
    private final native void dispatchMessageToContainerSlotImpl(final String message, final OPParamsJS params) /*-{
        if (this.messageFromEmbeddedViewHandlerCallback != null) {
            this.messageFromEmbeddedViewHandlerCallback(message, params);
        }
    }-*/;

    /**
     * Inform the view proxy, on the slot side, that the embedded view has been attached.
     */
    public final void onEmbeddedViewAttach() {
        onEmbeddedViewAttachImpl();
    }
    
    /**
     * Real implementation of informing the view proxy, on the slot side, that the embedded view has been attached.
     */
    private final native void onEmbeddedViewAttachImpl() /*-{
        if (this.onEmbeddedViewAttachCallback != null) {
            this.onEmbeddedViewAttachCallback();
        }
    }-*/;
    
    /**
     * Attach the dispatcher to the given element. If there was already a dispatcher attached to the element, override the dispatcher with the new one.
     * 
     * @param element
     *            the element on which to attach the dispatcher. Shouldn't be null.
     */
    public final void attachDispatcherOnElement(final Element element) {
        assert element != null : "parameter element shouldn't be null";

        attachDispatcherOnElementImpl(this, element);
    }

    /**
     * Real implementation of attaching the dispatcher to an element.
     * 
     * @param dispatcher
     *            the dispatcher to attach
     * @param element
     *            the element on which to attach the dispatcher.
     */
    private final native void attachDispatcherOnElementImpl(final OPMessagesDispatcher dispatcher, final Element element) /*-{
        element.messageDispatcher = dispatcher;
    }-*/;

    /**
     * Returns the dispatcher attached to the element with the given id. Returns null if there is no element with this id, or if there is no dispatcher attached
     * to the element.
     * 
     * @param id
     *            the id of the element on which a dispatcher should be found. Shouldn't be null, shouldn't be empty.
     * @return the dispatcher attached to the element specified by the given id, or null if there is no element with this id, or if there is no dispatcher
     *         attached to this element.
     */
    public final static OPMessagesDispatcher getDispatcherAttachedToId(final String id) {
        assert id != null : "parameter id shouldn't be null";
        assert !id.isEmpty() : "parameter id shouldn't be empty";

        final Element element = DOM.getElementById(id);
        if (element == null) {
            return null;
        }

        return getDispatcherAttachedToElement(element);
    }

    /**
     * Real implementation of returning the dispatcher attached to the given element.
     * 
     * @param element
     *            the element on which to search for a dispatcher
     * @return the attached dispatcher, if any, or null if there is no dispatcher attached to this element.
     */
    private final native static OPMessagesDispatcher getDispatcherAttachedToElement(final Element element) /*-{
        return element.messageDispatcher;
    }-*/;
    
    /**
     * Register the given slot proxy in this dispatcher as handler of message from container slot.
     * 
     * @param proxy
     *            the proxy to register on the dispatcher. Shouldn't be null.
     */
    public final void registerSlotProxy(final OPSlotProxy proxy) {
        assert proxy != null : "parameter proxy shouldn't be null";
        
        registerSlotProxyImpl(this, proxy);
    }
    
    /**
     * Real implementation of registering a slot proxy in this dispatcher as handler of message from container slot.
     * 
     * @param dispatcher
     *            the dispatcher on which to register the proxy.
     * @param proxy
     *            the proxy to register on the dispatcher.
     */
    private final native void registerSlotProxyImpl(final OPMessagesDispatcher dispatcher, final OPSlotProxy proxy) /*-{
        dispatcher.messageFromContainerSlotHandlerCallback = function(message, paramsjs) {
            return $entry(proxy.@fr.openparts.OpenParts.client.OPSlotProxy::handleMessageFromContainerSlot(Ljava/lang/String;Lfr/openparts/OpenParts/client/OPParamsJS;)(message, paramsjs));
        }
    }-*/;
    
    /**
     * Register the given view proxy in this dispatcher as handler of message from embedded view.
     * 
     * @param proxy
     *            the proxy to register on the dispatcher. Shouldn't be null.
     */
    public final void registerViewProxy(final OPViewProxy proxy) {
        assert proxy != null : "parameter proxy shouldn't be null";
        
        registerViewProxyImpl(this, proxy);
    }
    
    /**
     * Real implementation of registering a view proxy in the dispatcher as handler of message from embedded view.
     * 
     * @param dispatcher
     *            the dispatcher on which to register the proxy.
     * @param proxy
     *            the proxy to register on the dispatcher.
     */
    private final native void registerViewProxyImpl(final OPMessagesDispatcher dispatcher, final OPViewProxy proxy) /*-{
        dispatcher.messageFromEmbeddedViewHandlerCallback = function(message, paramsjs) {
            return $entry(proxy.@fr.openparts.OpenParts.client.OPViewProxy::handleMessageFromEmbeddedView(Ljava/lang/String;Lfr/openparts/OpenParts/client/OPParamsJS;)(message, paramsjs));
        }
        dispatcher.onEmbeddedViewAttachCallback = function() {
            return $entry(proxy.@fr.openparts.OpenParts.client.OPViewProxy::onEmbeddedViewAttach()());
        }
    }-*/;
    
}
